home *** CD-ROM | disk | FTP | other *** search
/ AGA Toolkit '97 / The AGA Toolkit '97.iso / miscellaneous / science / maths / calc / source / zio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-07  |  14.4 KB  |  661 lines

  1. /*
  2.  * Copyright (c) 1994 David I. Bell
  3.  * Permission is granted to use, distribute, or modify this source,
  4.  * provided that this copyright notice remains intact.
  5.  *
  6.  * Scanf and printf routines for arbitrary precision integers.
  7.  */
  8.  
  9. #include "stdarg.h"
  10. #include "zmath.h"
  11.  
  12.  
  13. #define    OUTBUFSIZE    200        /* realloc size for output buffers */
  14.  
  15. #define    PUTCHAR(ch)        math_chr(ch)
  16. #define    PUTSTR(str)        math_str(str)
  17. #define    PRINTF1(fmt, a1)    math_fmt(fmt, a1)
  18. #define    PRINTF2(fmt, a1, a2)    math_fmt(fmt, a1, a2)
  19.  
  20.  
  21. long    _outdigits_ = 20;        /* default digits for output */
  22. int    _outmode_ = MODE_INITIAL;    /* default output mode */
  23.  
  24.  
  25. /*
  26.  * Output state that has been saved when diversions are done.
  27.  */
  28. typedef struct iostate IOSTATE;
  29. struct iostate {
  30.     IOSTATE *oldiostates;        /* previous saved state */
  31.     long outdigits;            /* digits for output */
  32.     int outmode;            /* output mode */
  33.     FILE *outfp;            /* file unit for output (if any) */
  34.     char *outbuf;            /* output string buffer (if any) */
  35.     long outbufsize;        /* current size of string buffer */
  36.     long outbufused;        /* space used in string buffer */
  37.     BOOL outputisstring;        /* TRUE if output is to string buffer */
  38. };
  39.  
  40.  
  41. static IOSTATE    *oldiostates = NULL;    /* list of saved output states */
  42. static FILE    *outfp = NULL;        /* file unit for output */
  43. static char    *outbuf = NULL;        /* current diverted buffer */
  44. static BOOL    outputisstring = FALSE;
  45. static long    outbufsize;
  46. static long    outbufused;
  47.  
  48.  
  49. /*
  50.  * zio_init - perform needed initilization work
  51.  *
  52.  * On some systems, one cannot initialize a pointer to a FILE *.
  53.  * This routine, called once at startup is a work-a-round for
  54.  * systems with such bogons.
  55.  */
  56. void
  57. zio_init()
  58. {
  59.     static int done = 0;    /* 1 => routine already called */
  60.  
  61.     if (!done) {
  62.     outfp = stdout;
  63.     done = 1;
  64.     }
  65. }
  66.  
  67.  
  68. /*
  69.  * Routine to output a character either to a FILE
  70.  * handle or into a string.
  71.  */
  72. void
  73. math_chr(ch)
  74.     int ch;
  75. {
  76.     char    *cp;
  77.  
  78.     if (!outputisstring) {
  79.         fputc(ch, outfp);
  80.         return;
  81.     }
  82.     if (outbufused >= outbufsize) {
  83.         cp = (char *)realloc(outbuf, outbufsize + OUTBUFSIZE + 1);
  84.         if (cp == NULL)
  85.             math_error("Cannot realloc output string");
  86.         outbuf = cp;
  87.         outbufsize += OUTBUFSIZE;
  88.     }
  89.     outbuf[outbufused++] = (char)ch;
  90. }
  91.  
  92.  
  93. /*
  94.  * Routine to output a null-terminated string either
  95.  * to a FILE handle or into a string.
  96.  */
  97. void
  98. math_str(str)
  99.     char    *str;
  100. {
  101.     char    *cp;
  102.     int    len;
  103.  
  104.     if (!outputisstring) {
  105.         fputs(str, outfp);
  106.         return;
  107.     }
  108.     len = strlen(str);
  109.     if ((outbufused + len) > outbufsize) {
  110.         cp = (char *)realloc(outbuf, outbufsize + len + OUTBUFSIZE + 1);
  111.         if (cp == NULL)
  112.             math_error("Cannot realloc output string");
  113.         outbuf = cp;
  114.         outbufsize += (len + OUTBUFSIZE);
  115.     }
  116.     memcpy(&outbuf[outbufused], str, len);
  117.     outbufused += len;
  118. }
  119.  
  120.  
  121. /*
  122.  * Output a null-terminated string either to a FILE handle or into a string,
  123.  * padded with spaces as needed so as to fit within the specified width.
  124.  * If width is positive, the spaces are added at the front of the string.
  125.  * If width is negative, the spaces are added at the end of the string.
  126.  * The complete string is always output, even if this overflows the width.
  127.  * No characters within the string are handled specially.
  128.  */
  129. void
  130. math_fill(str, width)
  131.     char *str;
  132.     long width;
  133. {
  134.     if (width > 0) {
  135.         width -= strlen(str);
  136.         while (width-- > 0)
  137.             PUTCHAR(' ');
  138.         PUTSTR(str);
  139.     } else {
  140.         width += strlen(str);
  141.         PUTSTR(str);
  142.         while (width++ < 0)
  143.             PUTCHAR(' ');
  144.     }
  145. }
  146.  
  147.  
  148. /*
  149.  * Routine to output a printf-style formatted string either
  150.  * to a FILE handle or into a string.
  151.  */
  152. #ifdef VARARGS
  153. # define VA_ALIST fmt, va_alist
  154. # define VA_DCL char *fmt; va_dcl
  155. #else
  156. # if defined(__STDC__) && __STDC__ == 1
  157. #  define VA_ALIST char *fmt, ...
  158. #  define VA_DCL
  159. # else
  160. #  define VA_ALIST fmt
  161. #  define VA_DCL char *fmt;
  162. # endif
  163. #endif
  164. /*VARARGS*/
  165. void
  166. math_fmt(VA_ALIST)
  167.     VA_DCL
  168. {
  169.     va_list ap;
  170.     char buf[200];
  171.  
  172. #ifdef VARARGS
  173.     va_start(ap);
  174. #else
  175.     va_start(ap, fmt);
  176. #endif
  177.     vsprintf(buf, fmt, ap);
  178.     va_end(ap);
  179.     math_str(buf);
  180. }
  181.  
  182.  
  183. /*
  184.  * Flush the current output stream.
  185.  */
  186. void
  187. math_flush()
  188. {
  189.     if (!outputisstring)
  190.         fflush(outfp);
  191. }
  192.  
  193.  
  194. /*
  195.  * Divert further output so that it is saved into a string that will be
  196.  * returned later when the diversion is completed.  The current state of
  197.  * output is remembered for later restoration.  Diversions can be nested.
  198.  * Output diversion is only intended for saving output to "stdout".
  199.  */
  200. void
  201. math_divertio()
  202. {
  203.     register IOSTATE *sp;
  204.  
  205.     sp = (IOSTATE *) malloc(sizeof(IOSTATE));
  206.     if (sp == NULL)
  207.         math_error("No memory for diverting output");
  208.     sp->oldiostates = oldiostates;
  209.     sp->outdigits = _outdigits_;
  210.     sp->outmode = _outmode_;
  211.     sp->outfp = outfp;
  212.     sp->outbuf = outbuf;
  213.     sp->outbufsize = outbufsize;
  214.     sp->outbufused = outbufused;
  215.     sp->outputisstring = outputisstring;
  216.  
  217.     outbufused = 0;
  218.     outbufsize = 0;
  219.     outbuf = (char *) malloc(OUTBUFSIZE + 1);
  220.     if (outbuf == NULL)
  221.         math_error("Cannot allocate divert string");
  222.     outbufsize = OUTBUFSIZE;
  223.     outputisstring = TRUE;
  224.     oldiostates = sp;
  225. }
  226.  
  227.  
  228. /*
  229.  * Undivert output and return the saved output as a string.  This also
  230.  * restores the output state to what it was before the diversion began.
  231.  * The string needs freeing by the caller when it is no longer needed.
  232.  */
  233. char *
  234. math_getdivertedio()
  235. {
  236.     register IOSTATE *sp;
  237.     char *cp;
  238.  
  239.     sp = oldiostates;
  240.     if (sp == NULL)
  241.         math_error("No diverted state to restore");
  242.     cp = outbuf;
  243.     cp[outbufused] = '\0';
  244.     oldiostates = sp->oldiostates;
  245.     _outdigits_ = sp->outdigits;
  246.     _outmode_ = sp->outmode;
  247.     outfp = sp->outfp;
  248.     outbuf = sp->outbuf;
  249.     outbufsize = sp->outbufsize;
  250.     outbufused = sp->outbufused;
  251.     outbuf = sp->outbuf;
  252.     outputisstring = sp->outputisstring;
  253.     return cp;
  254. }
  255.  
  256.  
  257. /*
  258.  * Clear all diversions and set output back to the original destination.
  259.  * This is called when resetting the global state of the program.
  260.  */
  261. void
  262. math_cleardiversions()
  263. {
  264.     while (oldiostates)
  265.         free(math_getdivertedio());
  266. }
  267.  
  268.  
  269. /*
  270.  * Set the output routines to output to the specified FILE stream.
  271.  * This interacts with output diversion in the following manner.
  272.  *    STDOUT    diversion    action
  273.  *    ----    ---------    ------
  274.  *    yes    yes        set output to diversion string again.
  275.  *    yes    no        set output to stdout.
  276.  *    no    yes        set output to specified file.
  277.  *    no    no        set output to specified file.
  278.  */
  279. void
  280. math_setfp(newfp)
  281.     FILE *newfp;
  282. {
  283.     outfp = newfp;
  284.     outputisstring = (oldiostates && (newfp == stdout));
  285. }
  286.  
  287.  
  288. /*
  289.  * Set the output mode for numeric output.
  290.  * This also returns the previous mode.
  291.  */
  292. int
  293. math_setmode(newmode)
  294.     int newmode;
  295. {
  296.     int oldmode;
  297.  
  298.     if ((newmode <= MODE_DEFAULT) || (newmode > MODE_MAX))
  299.         math_error("Setting illegal output mode");
  300.     oldmode = _outmode_;
  301.     _outmode_ = newmode;
  302.     return oldmode;
  303. }
  304.  
  305.  
  306. /*
  307.  * Set the number of digits for float or exponential output.
  308.  * This also returns the previous number of digits.
  309.  */
  310. long
  311. math_setdigits(newdigits)
  312.     long newdigits;
  313. {
  314.     long olddigits;
  315.  
  316.     if (newdigits < 0)
  317.         math_error("Setting illegal number of digits");
  318.     olddigits = _outdigits_;
  319.     _outdigits_ = newdigits;
  320.     return olddigits;
  321. }
  322.  
  323.  
  324. /*
  325.  * Print an integer value as a hex number.
  326.  * Width is the number of columns to print the number in, including the
  327.  * sign if required.  If zero, no extra output is done.  If positive,
  328.  * leading spaces are typed if necessary. If negative, trailing spaces are
  329.  * typed if necessary.  The special characters 0x appear to indicate the
  330.  * number is hex.
  331.  */
  332. /*ARGSUSED*/
  333. void
  334. zprintx(z, width)
  335.     ZVALUE z;
  336.     long width;
  337. {
  338.     register HALF *hp;    /* current word to print */
  339.     int len;        /* number of halfwords to type */
  340.     char *str;
  341.  
  342.     if (width) {
  343.         math_divertio();
  344.         zprintx(z, 0L);
  345.         str = math_getdivertedio();
  346.         math_fill(str, width);
  347.         free(str);
  348.         return;
  349.     }
  350.     len = z.len - 1;
  351.     if (zisneg(z))
  352.         PUTCHAR('-');
  353.     if ((len == 0) && (*z.v <= (FULL) 9)) {
  354.         len = '0' + *z.v;
  355.         PUTCHAR(len);
  356.         return;
  357.     }
  358.     hp = z.v + len;
  359.     PRINTF1("0x%x", (FULL) *hp--);
  360.     while (--len >= 0)
  361.         PRINTF1("%04x", (FULL) *hp--);
  362. }
  363.  
  364.  
  365. /*
  366.  * Print an integer value as a binary number.
  367.  * The special characters 0b appear to indicate the number is binary.
  368.  */
  369. /*ARGSUSED*/
  370. void
  371. zprintb(z, width)
  372.     ZVALUE z;
  373.     long width;
  374. {
  375.     register HALF *hp;    /* current word to print */
  376.     int len;        /* number of halfwords to type */
  377.     HALF val;        /* current value */
  378.     HALF mask;        /* current mask */
  379.     int didprint;        /* nonzero if printed some digits */
  380.     int ch;            /* current char */
  381.     char *str;
  382.  
  383.     if (width) {
  384.         math_divertio();
  385.         zprintb(z, 0L);
  386.         str = math_getdivertedio();
  387.         math_fill(str, width);
  388.         free(str);
  389.         return;
  390.     }
  391.     len = z.len - 1;
  392.     if (zisneg(z))
  393.         PUTCHAR('-');
  394.     if ((len == 0) && (*z.v <= (FULL) 1)) {
  395.         len = '0' + *z.v;
  396.         PUTCHAR(len);
  397.         return;
  398.     }
  399.     hp = z.v + len;
  400.     didprint = 0;
  401.     PUTSTR("0b");
  402.     while (len-- >= 0) {
  403.         val = *hp--;
  404.         mask = (1 << (BASEB - 1));
  405.         while (mask) {
  406.             ch = '0' + ((mask & val) != 0);
  407.             if (didprint || (ch != '0')) {
  408.                 PUTCHAR(ch);
  409.                 didprint = 1;
  410.             }
  411.             mask >>= 1;
  412.         }
  413.     }
  414. }
  415.  
  416.  
  417. /*
  418.  * Print an integer value as an octal number.
  419.  * The number begins with a leading 0 to indicate that it is octal.
  420.  */
  421. /*ARGSUSED*/
  422. void
  423. zprinto(z, width)
  424.     ZVALUE z;
  425.     long width;
  426. {
  427.     register HALF *hp;    /* current word to print */
  428.     int len;        /* number of halfwords to type */
  429.     int num1='0', num2='0';    /* numbers to type */
  430.     int rem;        /* remainder number of halfwords */
  431.     char *str;
  432.  
  433.     if (width) {
  434.         math_divertio();
  435.         zprinto(z, 0L);
  436.         str = math_getdivertedio();
  437.         math_fill(str, width);
  438.         free(str);
  439.         return;
  440.     }
  441.     if (zisneg(z))
  442.         PUTCHAR('-');
  443.     len = z.len;
  444.     if ((len == 1) && (*z.v <= (FULL) 7)) {
  445.         num1 = '0' + *z.v;
  446.         PUTCHAR(num1);
  447.         return;
  448.     }
  449.     hp = z.v + len - 1;
  450.     rem = len % 3;
  451.     switch (rem) {    /* handle odd amounts first */
  452.         case 0:
  453.             num1 = (((FULL) hp[0]) << 8) + (((FULL) hp[-1]) >> 8);
  454.             num2 = (((FULL) (hp[-1] & 0xff)) << 16) + ((FULL) hp[-2]);
  455.             rem = 3;
  456.             break;
  457.         case 1:
  458.             num1 = 0;
  459.             num2 = (FULL) hp[0];
  460.             break;
  461.         case 2:
  462.             num1 = (((FULL) hp[0]) >> 8);
  463.             num2 = (((FULL) (hp[0] & 0xff)) << 16) + ((FULL) hp[-1]);
  464.             break;
  465.     }
  466.     if (num1)
  467.         PRINTF2("0%o%08o", num1, num2);
  468.     else
  469.         PRINTF1("0%o", num2);
  470.     len -= rem;
  471.     hp -= rem;
  472.     while (len > 0) {    /* finish in groups of 3 halfwords */
  473.         num1 = (((FULL) hp[0]) << 8) + (((FULL) hp[-1]) >> 8);
  474.         num2 = (((FULL) (hp[-1] & 0xff)) << 16) + ((FULL) hp[-2]);
  475.         PRINTF2("%08o%08o", num1, num2);
  476.         hp -= 3;
  477.         len -= 3;
  478.     }
  479. }
  480.  
  481.  
  482. /*
  483.  * Print a decimal integer to the terminal.
  484.  * This works by dividing the number by 10^2^N for some N, and
  485.  * then doing this recursively on the quotient and remainder.
  486.  * Decimals supplies number of decimal places to print, with a decimal
  487.  * point at the right location, with zero meaning no decimal point.
  488.  * Width is the number of columns to print the number in, including the
  489.  * decimal point and sign if required.  If zero, no extra output is done.
  490.  * If positive, leading spaces are typed if necessary. If negative, trailing
  491.  * spaces are typed if necessary.  As examples of the effects of these values,
  492.  * (345,0,0) = "345", (345,2,0) = "3.45", (345,5,8) = "  .00345".
  493.  */
  494. void
  495. zprintval(z, decimals, width)
  496.     ZVALUE z;        /* number to be printed */
  497.     long decimals;        /* number of decimal places */
  498.     long width;        /* number of columns to print in */
  499. {
  500.     int depth;        /* maximum depth */
  501.     int n;            /* current index into array */
  502.     int i;            /* number to print */
  503.     long leadspaces;    /* number of leading spaces to print */
  504.     long putpoint;        /* digits until print decimal point */
  505.     long digits;        /* number of digits of raw number */
  506.     BOOL output;        /* TRUE if have output something */
  507.     BOOL neg;        /* TRUE if negative */
  508.     ZVALUE quo, rem;    /* quotient and remainder */
  509.     ZVALUE leftnums[32];    /* left parts of the number */
  510.     ZVALUE rightnums[32];    /* right parts of the number */
  511.  
  512.     if (decimals < 0)
  513.         decimals = 0;
  514.     if (width < 0)
  515.         width = 0;
  516.     neg = (z.sign != 0);
  517.  
  518.     leadspaces = width - neg - (decimals > 0);
  519.     z.sign = 0;
  520.     /*
  521.      * Find the 2^N power of ten which is greater than or equal
  522.      * to the number, calculating it the first time if necessary.
  523.      */
  524.     _tenpowers_[0] = _ten_;
  525.     depth = 0;
  526.     while ((_tenpowers_[depth].len < z.len) || (zrel(_tenpowers_[depth], z) <= 0)) {
  527.         depth++;
  528.         if (_tenpowers_[depth].len == 0)
  529.             zsquare(_tenpowers_[depth-1], &_tenpowers_[depth]);
  530.     }
  531.     /*
  532.      * Divide by smaller 2^N powers of ten until the parts are small
  533.      * enough to output.  This algorithm walks through a binary tree
  534.      * where each node is a piece of the number to print, and such that
  535.      * we visit left nodes first.  We do the needed recursion in line.
  536.      */
  537.     digits = 1;
  538.     output = FALSE;
  539.     n = 0;
  540.     putpoint = 0;
  541.     rightnums[0].len = 0;
  542.     leftnums[0] = z;
  543.     for (;;) {
  544.         while (n < depth) {
  545.             i = depth - n - 1;
  546.             zdiv(leftnums[n], _tenpowers_[i], &quo, &rem);
  547.             if (!ziszero(quo))
  548.                 digits += (1L << i);
  549.             n++;
  550.             leftnums[n] = quo;
  551.             rightnums[n] = rem;
  552.         }
  553.         i = leftnums[n].v[0];
  554.         if (output || i || (n == 0)) {
  555.             if (!output) {
  556.                 output = TRUE;
  557.                 if (decimals > digits)
  558.                     leadspaces -= decimals;
  559.                 else
  560.                     leadspaces -= digits;
  561.                 while (--leadspaces >= 0)
  562.                     PUTCHAR(' ');
  563.                 if (neg)
  564.                     PUTCHAR('-');
  565.                 if (decimals) {
  566.                     putpoint = (digits - decimals);
  567.                     if (putpoint <= 0) {
  568.                         PUTCHAR('.');
  569.                         while (++putpoint <= 0)
  570.                             PUTCHAR('0');
  571.                         putpoint = 0;
  572.                     }
  573.                 }
  574.             }
  575.             i += '0';
  576.             PUTCHAR(i);
  577.             if (--putpoint == 0)
  578.                 PUTCHAR('.');
  579.         }
  580.         while (rightnums[n].len == 0) {
  581.             if (n <= 0)
  582.                 return;
  583.             if (leftnums[n].len)
  584.                 zfree(leftnums[n]);
  585.             n--;
  586.         }
  587.         zfree(leftnums[n]);
  588.         leftnums[n] = rightnums[n];
  589.         rightnums[n].len = 0;
  590.     }
  591. }
  592.  
  593.  
  594. /*
  595.  * Read an integer value in decimal, hex, octal, or binary.
  596.  * Hex numbers are indicated by a leading "0x", binary with a leading "0b",
  597.  * and octal by a leading "0".  Periods are skipped over, but any other
  598.  * extraneous character stops the scan.
  599.  */
  600. void
  601. atoz(s, res)
  602.     register char *s;
  603.     ZVALUE *res;
  604. {
  605.     ZVALUE z, ztmp, digit;
  606.     HALF digval;
  607.     BOOL minus;
  608.     long shift;
  609.  
  610.     minus = FALSE;
  611.     shift = 0;
  612.     if (*s == '+')
  613.         s++;
  614.     else if (*s == '-') {
  615.         minus = TRUE;
  616.         s++;
  617.     }
  618.     if (*s == '0') {        /* possibly hex, octal, or binary */
  619.         s++;
  620.         if ((*s >= '0') && (*s <= '7')) {
  621.             shift = 3;
  622.         } else if ((*s == 'x') || (*s == 'X')) {
  623.             shift = 4;
  624.             s++;
  625.         } else if ((*s == 'b') || (*s == 'B')) {
  626.             shift = 1;
  627.             s++;
  628.         }
  629.     }
  630.     digit.v = &digval;
  631.     digit.len = 1;
  632.     digit.sign = 0;
  633.     z = _zero_;
  634.     while (*s) {
  635.         digval = *s++;
  636.         if ((digval >= '0') && (digval <= '9'))
  637.             digval -= '0';
  638.         else if ((digval >= 'a') && (digval <= 'f') && shift)
  639.             digval -= ('a' - 10);
  640.         else if ((digval >= 'A') && (digval <= 'F') && shift)
  641.             digval -= ('A' - 10);
  642.         else if (digval == '.')
  643.             continue;
  644.         else
  645.             break;
  646.         if (shift)
  647.             zshift(z, shift, &ztmp);
  648.         else
  649.             zmuli(z, 10L, &ztmp);
  650.         zfree(z);
  651.         zadd(ztmp, digit, &z);
  652.         zfree(ztmp);
  653.     }
  654.     ztrim(&z);
  655.     if (minus && !ziszero(z))
  656.         z.sign = 1;
  657.     *res = z;
  658. }
  659.  
  660. /* END CODE */
  661.